/*
 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     bstefanescu
 */
package org.nuxeo.gwt.habyt.templates.rebind;

import java.io.PrintWriter;
import java.lang.reflect.Field;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;

/**
 * 
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 * 
 */
public class WidgetRegistryGenerator extends Generator {

    @Override
    public String generate(TreeLogger logger, GeneratorContext context,
            String typeName) throws UnableToCompleteException {

        TypeOracle typeOracle = context.getTypeOracle();
        assert (typeOracle != null);

        JClassType baseType = typeOracle.findType(typeName);
        if (baseType == null) {
            logger.log(TreeLogger.ERROR, "Unable find type " + typeName, null);
            throw new UnableToCompleteException();
        }
        String packageName = baseType.getPackage().getName();
        String simpleName = "_WidgetRegistryImpl";
        PrintWriter printWriter = context.tryCreate(logger, packageName,
                simpleName);
        if (printWriter == null) { // already generated
            return packageName + "." + simpleName;
        }

        JClassType factoryBaseType = typeOracle.findType(packageName
                + ".WidgetFactory");
        JClassType[] types = factoryBaseType.getSubtypes();

        // Generate the source file
        ClassSourceFileComposerFactory composer = new ClassSourceFileComposerFactory(
                packageName, simpleName);
        composer.setSuperclass(packageName + ".WidgetRegistry");
        // composer.addImport("java.util.*");
        SourceWriter out = composer.createSourceWriter(context, printWriter);
        out.println("public " + simpleName + "() {");
        out.indent();
        for (JClassType type : types) {
            if (type.isDefaultInstantiable()) {
                out.println("addFactory(new " + type.getQualifiedSourceName()
                        + "());");
            }
        }
        out.outdent();
        out.println("}");
        // printGeneratedCode(printWriter);
        out.commit(logger);

        return packageName + "." + simpleName;
    }

    /**
     * Must be called before out.commit(logger)
     * 
     * @param pw
     * @return
     */
    public static String getGeneratedCode(PrintWriter pw) {
        try {
            Field field = PrintWriter.class.getDeclaredField("out");
            field.setAccessible(true);
            return field.get(pw).toString();
        } catch (Throwable t) {
            throw new RuntimeException(
                    "Failed to get generated code - the current GWT compiler is may be not supported",
                    t);
        }
    }

    public static void printGeneratedCode(PrintWriter pw) {
        System.out.println("===================================");
        System.out.println(getGeneratedCode(pw));
        System.out.println("===================================");
    }
}
